軟體工程是計算機科學的一門分支,目的是尋找能引導大型且複雜軟體系統開發的原則,開發這樣的系統所要面的問題比撰寫小型程式大得多,比如開發一個大型系統需要多人一起合作很長的時間,在期間所提出的系統需求也可能會隨時調整,並且合作的人員也會有所改變,因此軟體工程包含人員與專案的管理,不過這個議題比較像是商業管理領域,本章只著重在計算機科學議題上。
在工程學中由於他是個非常完善的領域,所以會有許多之前就發展好的工程方法可以用來處理問題,但軟體開發的特性與工程領域有著基本的不同,其中一項差異是從預先建置好的通用元件來建構系統的能力,當要建構一個複雜的設備時,傳統的工程領域長久以來都使用現成的元件作為基本構建,比如要設計一部新車,設計師不需要設計新的引擎或變速箱只要使用那些已經設計好的零件,但在軟體工程來說這個部分相當落後,過去設計的軟體元件都屬於訂製款所以很難讓別人使用,使得複雜的軟體系統通常都是從頭開始設計。
另一個軟體工程與其他工程的差異是缺乏用來評估軟體特性的評量法 (metrics)
,比如要估算軟體開發的成本,若想要估算計畫中的產品複雜度就缺乏可靠的軟體複雜度評估法,同樣的評估軟體的品質也是一個大挑戰。
因此軟體工程的研究分為兩個層次進行,有些研究者(實務派)發展能夠立即應用的開發技術,而其他研究者(理論派)則尋找新的原理或理論,不論那個派系都急需新的進展,軟體錯誤已經發展成會造成嚴重災害的程度了,比如誤認錯的目標為攻擊目標或讓銀行損失個幾百萬等等。
不過情況也不是非常悲觀,電腦科技對於軟體發展也產生了所謂的電腦輔助軟體工程 (computer-aided software engineering, CASE)
讓軟體開發程序變得更有效率,有些工具是專門為了軟體工程設計的比如說有種名為整合開發環境 (integrated development environment, IDE)
的系統結合了軟體發展的工具(編輯器, 編譯器或除錯工具等等) 成為單一整合工具。
大型系統開發的挑戰之一是需要集合眾人之力,而團體合作進行一個大型專案的個階段都需要各種不同的技能,使得程式設計師可以單獨撰寫程式以降低每個人所負責的任務與複雜性,事實上是因為大型軟體系統非常複雜所以要將其分割程序多小型的元件好讓每個小元件可讓每個程式設計師完全掌控。
軟體工程中最重要的概念就是軟體的生命週期。
一但軟體完成後他就會進入使用與被維護的循環,這個循環會在軟體的使用期間不斷的重複,許多工廠所製造的產品也有這種循環,不過兩這的差異在於其他產品的維護階段往往是修理的過程,但軟體維護階段包含更正
與更新
,實際上軟體維護階段是因為發現錯誤或因為軟體因應應用上的改變而需要進行調整,或是在上次改版的改變造成其他方面出了問題。
無論什麼原因造成軟體需要進入維護階段都會需要安排一個人力去研究其程式碼或相關文件,直到了解該程式並找到問題所在,但即使軟體設計良好且文件也清楚的情況下,要了解其他人的程式碼也是個不容易的工作,因此多數軟體工程的研究都專注於軟體生命週期的發展階段希望藉此提高程式效益。
傳統軟體開發的生命週期主要步驟是軟體需求分析
, 設計
, 實作
與測試
。
軟體生命週期始於軟體需求分析,目的是明確定界定軟體的服務項目,並指明這個服務的任何條件(時間限制, 安全性等等),其分析包含從相關人士 (stakeholder)
取得重要意見,軟體需求分析的過程包含收集與分析軟體使用者的需求與軟體專案相關人士進行軟體期望, 需求, 成本與可行性之間平衡的協商並最後發展出明確的指名軟體必須具備的特徵和服務,這些需求應記錄在軟體需求規格書 (software requirements specification)
的文件中。
從軟體開發這的角度來說軟體需求規格書應該定義軟體開發的明確目標,然而規格書中恨少會有明確的目標,事實上多數軟體工程領域的實務派認為溝通不良與軟體需求的變動是成本超支與軟體延遲交付的主要原因。
軟體需求分析對軟體產品進行描述,而設計則是對該軟體的建構提出規劃,所以說需求分析是找出要解決的問題而設計是開發出問題的解法
,軟體系統的內部結構是在設計階段中建立的,設計階段的結果代表軟體系統的結構有清楚的描述。
實作包含程式的撰寫
、資檔的產生
以及資料庫的開發
,在實作階段可以看出軟體分析師 (softwaren analyst)
與程式設計師 (programmer)
任務上的區別,軟體分析師參與了軟體整個開發過程但會比較注重在需求分析與設計階段,而軟體設計師則主要參與實作階段。
在整個開發過程的每個步驟中都需要進行準確的測試,測試是確保軟體品質的必要程序之一,而確保軟體品質是整個軟體生命週期的目標,因此許多軟體工程師認為測試不該再視為軟體開發的一項單獨步驟,而是應該融入其他步驟中,因此軟體開發程序應該是軟體需求分析與確認 (requirements analysis and confirmation)
, 設計與驗證 (design and validation)
以及實作與測試 (implementation and testing)
。
早期軟體工程法堅持以嚴格且循序的方式來執行分析, 設計, 實作與測試,因此軟體工程師堅持在開始設計之前必須先完成整個軟體需求規格,同樣的在開始實作錢也要先完成設計,這種開發模式稱為瀑布模式 (waterfall model)
,因為開發過程只允許按照一種方式進行。
最近軟體工程開始有了改變,出現了一種稱為漸進模式 (incremental model)
的開發模式,這種模式認為軟體可以逐步構建出來,最初所完成的軟體是一個有限功能的簡化版本,一旦這個版本經過測試並由部分使用者評估過就會以漸進的方式加上其他功能並在進行測試直到系統全部完。
另一種不走瀑布模式的方式稱為反覆模式 (iterative model)
,這種模式實際上有時候等於漸進模式,但兩者的差異在於漸進模式會『拓展』
每個初步版本使得系統規模逐漸增大,而反覆模式則是『精煉』
每個初步版本,實務上漸進模式會包含反覆測試而反覆模式可能會逐步的加入新功能。
反覆模式法的一個重要實例是統一軟體開發過程 (rational unified process, RUP)
,RUP 的廣泛使用導致了另一種非營利版本的發展,這種版本稱為統一軟體 (unified process)
並提供非商業模式的免費版。
漸進模式和反覆模式有時候會利用軟體開發往原型化 (prototyping)
的趨勢來進行系統建置與評估,原型化指的是非完整版的系統,這個系統稱為原型,在漸進模式中這個原型會逐漸演進為完整的終極版,這個過程稱為演進式原型化 (evolutionary prototyping)
,而在反覆模式中圓形可能被捨棄使最終設計有新的實作,這種方式稱為丟棄式原型化 (throwaway prototyping)
,屬於丟棄式原型化的例子之一是快速原型化 (rqpid prototyping)
,這種方式能夠在軟體開發的早期階段快速地建構出預想中的系統簡單版,其目的並非一個能運作的產品而是作為一種說明工具以用來在軟體開發過程中對相關人士闡述系統特性。
在電腦愛好者之間有一種使用多年的方法,稱為開放原始碼發展法 (open-source development)
,是漸進模式與反寬魔是非正式的典型模式。透過這種方法產生了現今許多免費軟體,其中著名的例子就是 Linux
,軟體開放原始碼的開發過程為: 由一個程式設計師撰寫該軟體的初始版本然後將程式碼與相關文件放到網路上,之後這個軟體可以供他人下載使用,因為其他人也可以獲得這個軟體的原始碼與文件,所以可以對他進行修改以符合他們自己的需求過更正使用上發現的錯誤與問題,然後由開發者將這些更改發布在新的版本中。
與瀑布模式最大相逕庭的方式稱為敏捷法 (agile method)
,這是眾多方法所集合而成的模式,此模式對軟體需求改變進行快速反應並且不重視眼鏡的需求分析和設計,簡而言之敏捷法舉有高度彈性與瀑布式有強烈的對比。
若要修改軟體,程式設計師必需要了解該程式或相關部分的程式碼,要了解大型軟體系統程式碼幾乎是非常困難的情況,尤其是軟體沒有模組化 (modularity)
,所謂的模組化是將軟體劃分為適當大小的單元,一般稱為模組 (module)
,每一個模組都負責軟體某部分的功能。
模組化可以讓程式劃分為適當大小的單元,因此對程設的修改就只需要針對少數幾個模組即可,讓程式設計師可以專注於程式的某個部分而不用了解整個系統的程式,不過這也要基於這個模組的修改不影響到其他模組的前提下,因此當要設計模組化系統時應盡力設計出獨立的模組
,要盡量避免模組之間的連結亦即模組之間的耦合 (couping)
要盡量的少,軟體系統中不同模組之間的耦合程度可以看出這個軟體系統的複雜度。
模組之間的耦合有幾種不同的形式,其中一種是控制耦合 (control coupling)
,當某個模組使用到同一個資料項則其中一個模組對資料的異動可能會影響到另一個且資料本身的格式異動會同時影響到兩個模組。
兩個函數之間的耦合有兩種不同的形式,其中一種是由某個函數將資料傳給另一個函數,這種耦合在結構圖中是以箭頭
表示,兩個函數之間的箭頭會標示所傳送的資料而箭頭方向代表資料傳送的方向。
物件導向式程式設計的好處之一是他本質上就能讓物件之間的資料耦合發生率降到最低,因為物件中的方法一般都會包含物件內部資料的操作,由於不需要將在物件內部資料傳送給其他物件使得物件之間的資料耦合可以減少到最低。
相較於以參數傳傳送資料的形式,也有資料會以全域資料 (global data)
的形式讓模組共用,全域資料可以讓系統中所有模組直接取用,使用全域資料要非常小心,因為有人修改到某個全域資料的話會很難發現其他模組的更改狀況進而導致意想不到的錯誤出現。
與最小化耦合等同重要的就是最大化模組之間的內部連結這種連結稱為內聚 (cohesion)
,用來表示內部的連結換句話說內句是模組內部元素的相關性,如果有模組需要修改它內部的結構,可能會造成整體程序上的混淆或出意料之外的錯誤,因此除了要降低模組之間的耦合之外也要提高模組的內聚。
比較弱的內聚稱為邏輯內聚 (logical cohesion)
,這種內聚是由模組內部的元素做都執行類似的操作所形成,而比較強的內聚稱為功能內聚 (functional cohesion)
亦即模組內的所有元件都只專注在單一操作,在命令式設計中可以將子任務分中到其他模組以增強功能內聚,而在物件導向程式中肘個物件通常只有邏輯內聚
,因為物件內的方法通常會執行不相關的操作,唯一共通的連結是這些方法的操作都由同一個物件執行。
軟體設計師應該要盡力的讓物件中的每個方法都具有功能內聚,也就是將物件本身只有邏輯內聚而他內部的每個方法應該只執行一個功能內聚的任務。
模組設計的要法之一是掌握資訊隱藏 (information hiding)
的概念,它是指將資訊侷限於軟體系統的某個特定區域中
,這個隱藏的資料可以包含資料、資料型別、編碼法、模組的內部組成結構、程序單元的邏輯以及任何與模組內部特性相關的其他要素。
資訊隱藏的目的是避免模組的運作與其他模組產稱生不必要的相依性或影響,不然可能會影響到其他正常工作的模組或被錯誤的模組影響到,要注意的是資訊隱藏有兩種意義,一種是作為設計目的另一種是作為實作目的,模組的設計應該使其它模組不需要去存去其內部資源
,並且模組的實作應該加強其界限,前者的例子就是要最大化內聚
以及最小化耦合
,而後者的例子包含使用區域變數
、資料封裝
以及使用定義完善的控制結構
。
上面有提到軟體工程的領域中缺乏預先訂製好的構建已建立出大型軟體系統,不過軟體的模組化讓這個問題得以實現,因為物件導向的物件可以構成完整的程式並且具備清楚的介面讓個物件可以彼此溝通,不過雖然物件和類別可以作為軟體工程所需要的構建但不代表他們是完美的,其中一個問題是他們只能提供相對小型的構建,物件實際上只是元件 (component)
的特例,元件具有更一般化的概念是軟體中可重複使用的單元,實際上大多數元件都是基於物件導向程式法並且有一個以上的物件且都可以獨立完成某項工作。
本章會介紹一些軟體開發中分析與設計階段會用到的塑模法
與符號法
。
版本控制: 版本中系統現在已經是大多數軟體工程的一部分,透過網頁和網路的版本控制系統可提供一致的機制來追蹤程式碼的修改,讓多人開發合作編寫某個大型系統時可以更好的維護與開發,常見的版本控制工具有
Git
,Subversion
,Mercuurial
等等,版本控制可以精確追蹤是誰在何時做了哪些更改,且可以回朔原始碼到較早期的版本用於修改不當更改造成的錯誤或影響。
資料流程圖 (dataflow diagram)
是研究資料流向所取得的資訊的表現,箭頭代表資料的路徑,橢圓代表資料處理的位置點而矩形代表資料的來與和儲存。
資料流程圖不僅能在設計階段輔助辨認程序,在分析階段嘗試理解整個系統時也有很大的用處,建構資料流程圖可以加強客戶與軟體工程師之間的溝通。
另一種長年被使用的工具是資料字典 (data dictionary)
,這是用來儲存整個軟體系統中所用到的資料項目資訊,這個資訊裡面有資料的名稱
, 資料的有效構成元素 (資料只能是數字還是字母?數值的值域等等)
, 資料的儲存位置
以及資料使用在系統的什麼地方
。
建構資料字典的目的之一是強化軟體工程師與相關人士的溝通,因為資料字典可以在分析階段中幫忙確認一些問題而不用在設計或實作階段才發現,他的另一個目的是為了建立系統的一致性。
資料流程圖和資料字典是在物件導向出現前就存在的軟體工程工具,都是基於命令式程式法所發展出來的,而現代的工具稱為統一塑模語言 (Unified Modeling Language, UML)
,主要是隨著物件導向盛行而發展的,其中一個工具稱為使用案例圖 (use case diagram)
,他以一個大矩形來表示系統並描繪系統與使用者之間互動關係,這些互動稱為使用案例 (use case)
並以橢圓來表示使用案例,而系統使用者也稱為行動者 (actor)
會以火柴人表示,
使用案例圖是從外部檢視軟體洗桶架構,UML 也提供許多工具以描述系統內部的物件導向架構設計,其中一種稱為類別圖 (class diagram)
,這是一種表示類別結構和類別之間的關係符號表示法,類別關係在 UML 的術語中稱為關聯性 (association)
。
以上面的圖來說,通常會以矩形表示 Class
並且以線條表示關聯性
,關聯性線條不一定會有文字,除了指名類別之間的關聯性之外類別圖也能表達這些關聯性的數量,換句話說類別圖可以指名有多少個類別的實例與另一個類別的實例具有關聯性,關聯性的數量只會有三種形式 一對一關係
, 一對多關係
, 多對多關係
。
類別圖代表靜態的程式設計特性,但不代表執行時事件發生的順係,為了表達這種動態的特性 UML 提供許多不同的圖形表示法,這種表示法稱為互動圖 (interaction diagram)
,互動圖之一是順序圖 (sequence diagram)
,他會畫出執行某項動作所涉及到的個體之間的溝通,他們都以矩形表示個體並有虛線向下延伸,每個矩形與其虛線稱為生命線 (life line)
,每個個體之間個溝通已箭頭表示並連結彼此的生命線,其中會用文字標示請求的動作,這些箭頭按照時間順序從上而下看而順序圖會被一個大矩形包圍這個矩形稱為框架 (frame)
。
設計模式 (design pattern)
是軟體設計中一種預先開發好的模式,用來解決經常出現的問題,比如轉接器模式 (Adapter pattern)
所提供的方法可以用來解決使用預先訂製好的模組來建構軟體時會發生的問題,簡單來說如果有個模組可以用來解決目前手中的問題,但這個模組沒有與目前應用程式相容的介面,這時就可以使用轉接器模式將這個模組包在另一個模組裡面,這樣就能被應用程式使用。
軟體故障、成本超支和開發逾時等問題會在開發軟體系統中不斷的發生,這些問題都需要有方法來改善軟體的品質控制,本章將會介紹這個議題以及過去的一些成果。
現今大多數軟體是以測試來進行驗證,但除非我們可以測試到所有可能發生的狀況不然即使是做簡單的程式依然會有錯誤發生,因此測試所有可能性幾乎是不可能的任務。
基於帕雷托法則與基礎路徑測試的方法都需要了解軟體內部的組成,因此這兩種方法被歸類在白箱測試 (glass-box testing)
中,亦即軟體測試者需要了解軟體內部結構並根據對軟體的了解來進行測試。
不過軟體工程師發展出其他測試的方法能夠以有限個數的測試提高發現錯誤的機率,其中一種方式是基於軟體錯誤傾向再一起,亦即經驗告訴我們在大型系統中有少部分模組比較容易發生問題,藉由找出這些模組且充分測試這些模組就可以找到系統中大部分的問題與錯誤,這樣要比全面的測試所有模組來的有效率,這種方式就是帕雷托法則 (Pareto principle)
的實例之一,只要在關鍵區域多付出一些心力其效就可以快速增加。
另一種方法稱為基礎路徑測試 (basis path testing)
,這是用來確保軟體中每個指令至少都能執行到一次,這種方式使用數學中的圖輪法,雖然不能保證每種狀況都會被測試到但可以確保軟體中每一條陳述在測試過程中都會至少被執行到一次。
這種測試會在不了解軟體內部組成的情況下進行,簡而言之黑箱測試是從使用者角度出發,在黑箱測試中測試者並不會考量軟體是如何運行的,僅在意軟體是否能正確且即時的執行。
這是黑箱測試的方式之一,這種方式會包含一組指定範圍的資料,這些資料稱為等價類 (equivalence class)
,軟體以相同方式來執行這些資料人後再以接近邊界值的資料來測試軟體, 舉例來說如果某軟體的輸入值有特定的範圍那麼就應該要使用這個範圍的最大值與最小值來測試軟體,或著若是某軟體負責協調多項作業那麼就應該要以最多可能的運作項目才測試軟體,若軟體在這些測試項目中都能正常執行那麼軟體也應該能夠在所有項目中正常執行。
將軟體的前期版本給一群有興趣的使用者進行測試,目的是在軟體的最終版本還未穩固並上市之前可以知道軟體在實際應用上的狀況,如果測試是由開發端進行測試的話則稱為alpha 測試 (alphq testing)
,beta 測試的好處遠大於傳通的錯誤尋找法,從一般用戶反饋的意見來修改軟體以達成調整行銷策略的目的。
如果使用這無法知道軟體該怎麼使用或維護那麼這個軟體就沒有用處,所以文件算是軟體套件很重要的一部分,軟體文件有三個主要的方向分別對應文件的三種類別: 使用者文件
, 系統文件
和技術文件
。
使用者文件 (user documentation) 其目的是說明軟體的特色
與使用方式
,以應用層面的術語撰寫讓使用者閱讀的,好的使用者文件通常結合了設計完善的使用者介面,可以增加軟體的銷售量。
系統文件 (system docummentation) 其目的是描述軟體內部的組成以利於軟體在日後的維護,系統文件的主要內容是系統所有的程式碼,其中這些程式碼必須要易於閱讀,這也是為什麼現代軟體會使用高階語言並使用註釋以及使用模組化設計的原因。
系統文件的另一個要素是設計文件的紀錄,該紀錄包含軟體需求規格書以及這些規格設計的過程,在軟體的維護中這些資訊非常有用,因為他說明了這些軟體是如何設計的。
技術文件 (technical documentation) 其目的在說明如何安裝與維護軟體(如調整軟體運作參數、安裝更新以及回報錯誤等等)。
法律上賦予軟體所有權的範疇歸類於智慧財產權法 (intellectual property)
,著作權與專利權的目的在於讓產品的開發者可以在公開產品的同時取得所有權的保障,因此產品的開發者都會在相關產品上加上所有權的聲明,包含需求規格書、設計文件、程式碼、測試規劃以及最終產品的顯眼處,版權聲明會清楚的指名所有權,此外開發者權益會在軟體授權書 (software license)
上以法律用語正式的陳述,軟體授權書是軟體產品所有權人與使用者之間的法律協議,賦予使用這某些權限來使用該產品而不需要轉讓其智慧財產權給使用者,這個協議會詳細的說明雙方的權益與義務。
專利法是為了讓發明者可以從發明中獲取利潤,為了取得專利發明者需要證明該發明是新穎、有用且與其他發明者之發明無相似之處的,專利權年限一般是專利提申請後的 20 年。